﻿package  {
	import flash.display.MovieClip;
	import flash.events.MouseEvent;
	import flash.utils.getQualifiedClassName;
	import flash.utils.getDefinitionByName;
	import flash.geom.Point;
	
	public class Board extends MovieClip
	{
		var currentTurn:String;         // Turno do jogador
		var currentPiece:Object;        // Peça selecionada
		
		var boardPieces:Array;          // Array contendo as peças
		var boardSelection:Array;       // Array contendo as seleções
		var whitePieces:Array;          // Array contendo as peças brancas
		var blackPieces:Array;          // Array contendo as peças pretas
	
		var whiteKing:King;
		var blackKing:King;
		var enPassantPawn:Object;      // Último peão que utilizou o movimento duplo inicial.
		
		public static var squareSize:int = 65; //O tamanho da "casa" do tabuleiro.
		
		public function Board() 
		{
			currentTurn = "white";
			enPassantPawn = null;
			
			initContainers();
			createBoardPieces();
		}
		
		public function initContainers():void
		{
			boardPieces = new Array();
			boardSelection = new Array();
			whitePieces = new Array();
			blackPieces = new Array();
		}
		
		public function createBoardPieces():void
		{
			for (var i:int = 0; i < 8; i++) 
			{
				boardPieces[i] = new Array();
				boardSelection[i] = new Array();
				
				for (var j:int = 0; j < 8; j++) 
				{
					if(i == 0)
					{
						if (j == 0)
						{
							boardPieces[i][j] = new Hook("black", i, j, true);
							blackPieces.push(new Hook("black", i, j, true));
						}
						if (j == 1)
						{
							boardPieces[i][j] = new Horse("black", i, j, true);
							blackPieces.push(new Horse("black", i, j, true));
						}
						if (j == 2)
						{
							boardPieces[i][j] = new Bishop("black", i, j, true);
							blackPieces.push(new Bishop("black", i, j, true));
						}
						if (j == 3)
						{
							boardPieces[i][j] = new Queen("black", i, j, true);
							blackPieces.push(new Queen("black", i, j, true));
						}
						if (j == 4)
						{
							boardPieces[i][j] = new King("black", i, j, true);
							blackKing = new King("black", i, j, true);
							blackPieces.push(new King("black", i, j, true));
						}
						if (j == 5)
						{
							boardPieces[i][j] = new Bishop("black", i, j, true);
							blackPieces.push(new Bishop("black", i, j, true));
						}
						if (j == 6)
						{
							boardPieces[i][j] = new Horse("black", i, j, true);
							blackPieces.push(new Horse("black", i, j, true));
						}
						if (j == 7)
						{
							boardPieces[i][j] = new Hook("black", i, j, true);
							blackPieces.push(new Hook("black", i, j, true));
						}
					}
					else if (i == 1) 
					{
						boardPieces[i][j] = new Pawn("black", i, j, true);
						blackPieces.push(new Pawn("black", i, j, true));
					}
					else if (i == 6) 
					{
						boardPieces[i][j] = new Pawn("white", i, j, true);
						whitePieces.push(new Pawn("white", i, j, true));
					}
					else if(i == 7)
					{
						if (j == 0)
						{
							boardPieces[i][j] = new Hook("white", i, j, true);
							whitePieces.push(new Hook("white", i, j, true)); 
						}
						if (j == 1)
						{
							boardPieces[i][j] = new Horse("white", i, j, true);
							whitePieces.push(new Horse("white", i, j, true));
						}
						if (j == 2)
						{
							boardPieces[i][j] = new Bishop("white", i, j, true);
							whitePieces.push(new Bishop("white", i, j, true));
						}
						if (j == 3)
						{
							boardPieces[i][j] = new Queen("white", i, j, true);
							whitePieces.push(new Queen("white", i, j, true));
						}
						if (j == 4)
						{
							boardPieces[i][j] = new King("white", i, j, true);
							whiteKing = new King("white", i, j, true);
							whitePieces.push(new King("white", i, j, true));
						}
						if (j == 5)
						{
							boardPieces[i][j] = new Bishop("white", i, j, true);
							whitePieces.push(new Bishop("white", i, j, true));
						}
						if (j == 6)
						{
							boardPieces[i][j] = new Horse("white", i, j, true);
							whitePieces.push(new Horse("white", i, j, true));
						}
						if (j == 7)
						{
							boardPieces[i][j] = new Hook("white", i, j, true);
							whitePieces.push(new Hook("white", i, j, true));
						}
					}
					
					if (boardPieces[i][j] != null)
					{
						addChild(boardPieces[i][j]);
						boardPieces[i][j].addEventListener(MouseEvent.CLICK, selectPiece, false, 0, true);
					}
					
					boardSelection[i][j] = new Selection("unselected", i, j);
					addChild(boardSelection[i][j]);
					
				}
			}
		}
		
		// Seleciona a peça clicada e chama a função adequada.
		public function selectPiece(e:MouseEvent):void
		{
			if (currentPiece != null)
			{
				clearBoardSelection();
				
				if (currentPiece == e.target)
				{
					currentPiece = null;
				}
				else if (currentPiece.pieceColor == e.target.pieceColor)
				{
					currentPiece = e.target;
					showPossibleMoves( currentPiece.moves(boardPieces, enPassantPawn) );
				}
			}
			else if (turnAllowed(e.target))
			{
				currentPiece = e.target;
				showPossibleMoves( currentPiece.moves(boardPieces, enPassantPawn) );
				//currentPiece.moves retorna um array de movimentos possiveis, cada posição contendo um (x,y)
			}
		}
		
		public function showPossibleMoves(moves:Array)
		{
			var movesSize = moves.length;
			var pieceIndex:int;
			var selectionIndex:int;
			
			for( var i:int = 0; i < movesSize; i++)
			{
				boardSelection[moves[i].x][moves[i].y].gotoAndStop("selected");
				boardSelection[moves[i].x][moves[i].y].addEventListener(MouseEvent.CLICK, nextMove, false, 0, true);
				
				if (boardPieces[moves[i].x][moves[i].y] != null)
				{
				  pieceIndex = this.getChildIndex(boardPieces[moves[i].x][moves[i].y]);
				  selectionIndex = this.getChildIndex(boardSelection[moves[i].x][moves[i].y]);
				  
				  if(pieceIndex > selectionIndex)
				  	swapChildren(boardSelection[moves[i].x][moves[i].y], boardPieces[moves[i].x][moves[i].y]);
				}
			}
		}
		
		public function nextMove(e:MouseEvent):void
		{
			clearBoardSelection();
			
			if (!playerInCheck(e.target))
			{	
				changeCurrentPiecePosition(e.target);
				
				if (opponentInCheck())
				{
					// Verificar aqui o chequemate.
					trace("De [" + currentPiece.row + "][" + currentPiece.column + "] para [" + e.target.row + "][" + e.target.column + "], cheque!");
				}
				else
					trace("De [" + currentPiece.row + "][" + currentPiece.column + "] para [" + e.target.row + "][" + e.target.column + "]");
				
				changeTurn();
			}
			else
			{
				currentPiece = null;
				trace ("Movimento inválido! (o jogador não deve se colocar ou permanecer em cheque)");
			}
		}
		
		/*
			- futurePoint representa a posição que o jogador atual clicou para se movimentar.
			- futureBoard representa a disposição do tabuleiro se o jogador se movimentasse para o futurePoint
		*/
		public function playerInCheck(target:Object):Boolean
		{
			var className:String = flash.utils.getQualifiedClassName(currentPiece);
			var futurePoint:Point = new Point(target.row, target.column);
			var futureBoard:Array = changeFuturePiecePosition(target);
			
			var enemyPieces:Array;
			var playerKing:Point;
			
			if (currentTurn == "white")
			{
				enemyPieces = blackPieces;
				
				if (className == "King")
					playerKing = new Point(target.row, target.column);
				else
					playerKing = new Point(whiteKing.row, whiteKing.column)
			}
			else
			{
				enemyPieces = whitePieces;
				
				if (className == "King")
					playerKing = new Point(target.row, target.column);
				else
					playerKing = new Point(blackKing.row, blackKing.column);
			}
			
			// Número de casas movidas na horizontal
			var distanceMoved:int = target.column - currentPiece.column;
			
			// Checagem de roque
			if (className == "King" && Math.abs(distanceMoved) > 1)
			{
				var kingPreviousColumn; // Coluna anterior a posição final do rei.
				
				// Roque pela direita
				if (distanceMoved > 1)
					kingPreviousColumn = target.column - 1;
				else
					kingPreviousColumn = target.column + 1;
					
				// Teste para ver se ambas as casas em que o rei passa não o colocam em cheque
				if (!kingInCheck(enemyPieces, new Point(target.row, kingPreviousColumn), futureBoard, new Point(target.row, kingPreviousColumn)) &&
					!kingInCheck(enemyPieces, playerKing, futureBoard, futurePoint))
				{
					return false;
				}
				else
					return true;
			}
			else
			{
				return kingInCheck(enemyPieces, playerKing, futureBoard, futurePoint);
			}
		}
		
		public function opponentInCheck():Boolean
		{
			var playerPieces:Array;
			var enemyKing:Point;
			
			if (currentTurn == "white")
			{
				playerPieces = whitePieces;
				enemyKing = new Point(blackKing.row, blackKing.column);
			}
			else
			{
				playerPieces = blackPieces;
				enemyKing = new Point(whiteKing.row, whiteKing.column);
			}
			
			if (kingInCheck(playerPieces, enemyKing, boardPieces))
			{
				boardPieces[enemyKing.x][enemyKing.y].checked = true;
				return true;
			}
			else
				return false;
		}
		
		/* 
			- Função que verifica se o próprio jogador está em cheque.
			- futurePoint só existirá se a função estiver checando se o jogador atual se colocou em cheque.
		*/
		public function kingInCheck(pieces:Array, king:Point, board:Array, futurePoint:Point=null):Boolean
		{
			var moves:Array; // Array que armazena todas as jogadas possíveis de uma peça.
			var possibleMoves:int; // Número de jogadas específicas de uma peça.
			var numberOfPieces:int = pieces.length;
			
			for(var i:int = 0; i < numberOfPieces; i++)
			{
				if (futurePoint != null && pieces[i].row == futurePoint.x && pieces[i].column == futurePoint.y)
					continue;
					
				moves = pieces[i].moves(board, enPassantPawn);
				possibleMoves = moves.length;
				
				for(var j:int = 0; j < possibleMoves; j++)
				{
					if (moves[j].x == king.x && moves[j].y == king.y)
					{
						return true;
					}
				}
			}
			
			return false;
		}
		
		// Retorna como o tabuleiro ficará após um jogada.
		public function changeFuturePiecePosition(target:Object):Array
		{
			var futureBoard = cloneBoard(boardPieces);
			
			var className:String = flash.utils.getQualifiedClassName(currentPiece);
			var currentClass:Class = flash.utils.getDefinitionByName(className) as Class;
			
			futureBoard[currentPiece.row][currentPiece.column] = null;
			
			if (futureBoard[target.row][target.column] != null)
			{
				futureBoard[target.row][target.column] = null;
			}
			else if (className == "Pawn" && Math.abs((currentPiece.row - target.row) * (currentPiece.column - target.column)) == 1)
			{
				/* Ajuste para o En Passant */
				if (currentPiece.pieceColor == "white")
					futureBoard[target.row + 1][target.column] = null;
				else
					futureBoard[target.row - 1][target.column] = null;
			}
			
			futureBoard[target.row][target.column] = new currentClass(currentPiece.pieceColor, target.row, target.column, false);
			
			/* 
				Realizar o ajuste para o Roque se eu quiser colocar IA
			*/
			
			return futureBoard;
		}
		
		// Modifica permanentemente o tabuleiro após uma jogada.
		public function changeCurrentPiecePosition(target:Object):void
		{
			var className:String = flash.utils.getQualifiedClassName(currentPiece);
			var currentClass:Class = flash.utils.getDefinitionByName(className) as Class;
			
			// Cuida da peça selecionada
			boardPieces[currentPiece.row][currentPiece.column].removeEventListener(MouseEvent.CLICK, nextMove);
			removeChild(boardPieces[currentPiece.row][currentPiece.column])
			boardPieces[currentPiece.row][currentPiece.column] = null;
			
			// Se a peça selecionada comeu uma peça adversária.
			if (boardPieces[target.row][target.column] != null)
			{
				boardPieces[target.row][target.column].removeEventListener(MouseEvent.CLICK, nextMove);
				removeChild(boardPieces[target.row][target.column]);
				boardPieces[target.row][target.column] = null;
				
				if (currentTurn == "white")
					removePiece(blackPieces, target.row, target.column); 
				else
					removePiece(whitePieces, target.row, target.column);
			}
			else if (className == "Pawn" && Math.abs((currentPiece.row - target.row) * (currentPiece.column - target.column)) == 1)
			{
				/* Ajuste para o En Passant */
				var enPassantTarget:Point;
				
				if (currentPiece.pieceColor == "white")
				{
					enPassantTarget = new Point(target.row + 1, target.column);
					removePiece(blackPieces, enPassantTarget.x, enPassantTarget.y);
				}
				else
				{
					enPassantTarget = new Point(target.row - 1, target.column);
					removePiece(whitePieces, enPassantTarget.x, enPassantTarget.y);
				}
				
				boardPieces[enPassantTarget.x][enPassantTarget.y].removeEventListener(MouseEvent.CLICK, nextMove);
				removeChild(boardPieces[enPassantTarget.x][enPassantTarget.y]);
				boardPieces[enPassantTarget.x][enPassantTarget.y] = null;
			}
			
			boardPieces[target.row][target.column] = new currentClass(currentPiece.pieceColor, target.row, target.column, false);
			addChild(boardPieces[target.row][target.column]);
			boardPieces[target.row][target.column].addEventListener(MouseEvent.CLICK, selectPiece, false, 0, true);
			
			// Inicio do ajuste para Roque
			var distanceMoved:int = target.column - currentPiece.column;
			if (className == "King" && Math.abs(distanceMoved) > 1)
			{
				var hookCurrentColumn:int;
				var hookFutureColumn:int;
				
				if (distanceMoved > 1)
				{
					hookCurrentColumn = 7;
					hookFutureColumn = target.column - 1;
				}
				else
				{
					hookCurrentColumn = 0;
					hookFutureColumn = target.column + 1;
				}
				
				boardPieces[target.row][hookCurrentColumn].removeEventListener(MouseEvent.CLICK, nextMove);
				removeChild(boardPieces[target.row][hookCurrentColumn]);
				boardPieces[target.row][hookCurrentColumn] = null;
				
				boardPieces[target.row][hookFutureColumn] = new Hook(currentPiece.pieceColor, target.row, hookFutureColumn, false);
				addChild(boardPieces[target.row][hookFutureColumn]);
				boardPieces[target.row][hookFutureColumn].addEventListener(MouseEvent.CLICK, selectPiece, false, 0, true);
			}
			//Fim do ajuste para Roque
			
			//Ajustando a posição do ultimo peão que se moveu duas casas
			if (className == "Pawn" && Math.abs(target.row - currentPiece.row) == 2)
				enPassantPawn = new currentClass(currentPiece.pieceColor, target.row, target.column, false);
			else
				enPassantPawn = null;
			//Fim do ajuste para a condição de En Passant
			
			updatePositions(className, target);
		}
		
		// Remove a peça comida do array de peças do jogador.
		public function removePiece(pieces:Array, row:int, column:int):void
		{
			var piecesLenght:int = pieces.length;
			
			for(var i:int = 0; i < piecesLenght; i++)
			{
				if (pieces[i].row == row && pieces[i].column == column)
				{
					pieces.splice(i,1);
					break;
				}
			}
		}
		
		/*
			- Atualiza a posição da peça movimentada no array de peças do jogador.
			- Atualiza a posição do rei do jogador, se necessário.
		*/
		public function updatePositions(pieceType:String, target:Object):void
		{
			if (currentTurn == "white")
			{
					changePiecePosition(whitePieces, target.row, target.column);
					
					if (pieceType == "King") {
						whiteKing.row = target.row;
						whiteKing.column = target.column;
					}
			}
			else
			{
					changePiecePosition(blackPieces, target.row, target.column);
					
					if (pieceType == "King") {
						blackKing.row = target.row;
						blackKing.column = target.column;
					}
			}
		}
		
		// Atualiza a posição da peça movimentada no array de peças do jogador.
		public function changePiecePosition(pieces:Array, row:int, column:int):void
		{
			var piecesLenght:int = pieces.length;
			
			for(var i:int = 0; i < piecesLenght; i++)
			{
				if (pieces[i].row == currentPiece.row && pieces[i].column == currentPiece.column)
				{
					pieces[i].row = row;
					pieces[i].column = column;
					pieces[i].firstMove = false;
					break;
				}
			}
		}
		
		function cloneBoard(board:Array):Array
		{ 
			var newBoard:Array = new Array();
			var className:String;
			var currentClass:Class;
			
			for(var i:int = 0; i < 8; i++)
			{
				newBoard[i] = new Array();
				
				for(var j:int = 0; j < 8; j++)
				{
					if (board[i][j] == null)
						newBoard[i][j] = null;
					else
					{
						className = flash.utils.getQualifiedClassName(currentPiece);
						currentClass = flash.utils.getDefinitionByName(className) as Class;
						newBoard[i][j] = new currentClass(board[i][j].pieceColor, i, j, board[i][j].firstMove);
					}
				}
			}
			
			return newBoard; 
		}
		
		public function clearBoardSelection():void
		{
			for (var i:int = 0; i < 8; i++) 
			{
				for (var j:int = 0; j < 8; j++) 
				{
					boardSelection[i][j].gotoAndStop("unselected");
					boardSelection[i][j].removeEventListener(MouseEvent.CLICK, nextMove);
				}
			}
		}
		
		public function changeTurn():void
		{
			currentPiece = null;
			
			if (currentTurn == "white")
				currentTurn = "black"
			else
				currentTurn = "white"
		}
		
		// Checa se a peça clicada pertence ao jogador.
		public function turnAllowed(piece:Object):Boolean
		{
			if (piece.pieceColor != currentTurn)
				return false;
			
			return true;
		}
	}
}